#include "../server_deps.h"
#include "../server.h"
#include "../game_logic/game_cfg.h"
#include "proto_mgr.h"
#include "combat.h"
#include "player_data.h"
#include "player_mgr.h"
#include <sstream>

#include "share/util.h"
#include "share/clock.h"


//#define LOG_COMBAT
#ifdef LOG_COMBAT
    #define RANGE_LOG(a...) tracer_t<1> t(a)
    #define LOC_LOG(a...) tracer_t<1>::log(a)
#else
    #define RANGE_LOG(a...)
    #define LOC_LOG(a...)
#endif

static struct combatcookie_st {
	player_t* players[2];
	bool can_combatreward;
} s_cookie;

void g_combatcookie(player_t* player1, player_t* player2, int combatreward) {
	s_cookie.players[0] = player1;
	s_cookie.players[1] = player2;
	if (combatreward < 0)
		s_cookie.can_combatreward = (player1 && !player2) || (!player1 && player2);
	else
		s_cookie.can_combatreward = combatreward;

	if (s_cookie.can_combatreward)
		memset(player1->combatrewards, -1, sizeof(player1->combatrewards));
}


int32_t calc_value(int32_t valueid, combat_pet_t* be, combat_pet_t* by) {
    int32_t value = 0;
    const tagProtoValue* value_proto = MAProto(Value, valueid);
    if (NULL != value_proto) {
        if (0 == value_proto->who) {
            if (value_proto->damage)
                value = min(by->hp, by->clog().calced_damage);
            else
                value = by->att.get(EPetAtt(value_proto->att));
        } else {
            if (value_proto->damage)
                value = min(by->hp, by->clog().calced_damage);
            else
                value = be->att.get(EPetAtt(value_proto->att));
        }
        value *= value_proto->pct / 9999.0;
    }
    return value;
}

void for_each_sprite(combat_unit_t** units, function<void(combat_sprite_t*)> cb, bool reverse = false) {
    if (reverse) {
        for (int i = 0; i<2; ++i) {
            if (NULL != units[i] && NULL != units[i]->sprite)
                cb(units[i]->sprite);
        }
    } else {
        for (int i = 1; i>-1; --i) {
            if (NULL != units[i] && NULL != units[i]->sprite)
                cb(units[i]->sprite);
        }
    }
}

void for_each_pet(combat_unit_t*const* units, function<void(combat_pet_t*)> cb, bool reverse = false) {
    if (reverse) {
        for (int i=1; i>-1; --i)
            for (int j=2; j>-1; --j)
                if (NULL != units[i] && NULL != units[i]->pet[j])
                    cb(units[i]->pet[j]);

    } else {
        for (int i=0; i<2; ++i)
            for (int j=0; j<3; ++j)
                if (NULL != units[i] && NULL != units[i]->pet[j])
                    cb(units[i]->pet[j]);
    }
}

int live_num(combat_unit_t* unit) {
    int live = 0;
    for (int i=0; i<3; ++i) {
        auto pet = unit->pet[i];
        if (NULL != pet && !pet->is_dead())
            ++live;
    }
    return live;
}
int live_unit_num(combat_unit_t** units) {
    int live = 0;
    for (int i=0; i<2; ++i)
        if (live_num(units[i]) > 0)
            ++live;

    return live;
}
combat_pet_t* rand_target(combat_pet_t* caster, combat_unit_t** units) {
    int8_t side = 1-caster->unit->side;

    set<int8_t> live;
    for (int i=0; i<3; ++i) {
        auto pet = units[side]->pet[i];
        if (NULL != pet && !pet->is_dead())
            live.insert(i);
    }

    if (live.empty())
        return NULL;

    int8_t index = rand() % live.size();

    auto itr = live.begin();
    for (int i=0; i<index; ++i)
        ++itr;

    return units[side]->pet[*itr];
}

combat_pet_t* select_target(combat_pet_t* caster, combat_unit_t** units) {
    int8_t side = 1-caster->unit->side;

    set<int8_t> dead;
    set<int8_t> live;
    for (int i=0; i<3; ++i) {
        auto pet = units[side]->pet[i];
        if (NULL != pet && !pet->is_dead())
            live.insert(i);
        else
            dead.insert(i);
    }

    if (dead.size() == 3)
        return NULL;
    else if (dead.size() == 2)
        return units[side]->pet[*(live.begin())];
    else if (dead.size() == 1) {
    	if (dead.count(0) == 1) {
            if (rand() % 100 < 50)
                return units[side]->pet[*(live.begin())];
            else {
                auto itr = live.begin();
                ++itr;
                return units[side]->pet[*itr];
            }
    	} else {
            if (rand() % 100 < 90)
                return units[side]->pet[*(live.begin())];
            else {
                auto itr = live.begin();
                ++itr;
                return units[side]->pet[*itr];
            }
    	}
    } else {
        int8_t prob[3] = {86, 7, 7};
        int8_t num = rand() % 100;
        for (int i=0; i<3; ++i) {
            num -= prob[i];
            if (num < 0)
                return units[side]->pet[i];
        }

        return NULL;
    }
}
combat_pet_t* select_caster(combat_unit_t** units) {
    list<combat_pet_t*> attackers;

    combat_pet_t* multi_cast = NULL;
    for_each_pet(units, [&attackers, &multi_cast](combat_pet_t* pet){
        if (!pet->is_dead() && pet->act_num > 0) {
            if (pet->multi_cast)
                multi_cast = pet;
            else
                attackers.emplace_back(pet);
        }
    });

    if (NULL != multi_cast)
        return multi_cast;

    attackers.sort([](const combat_pet_t* lhs, const combat_pet_t* rhs)->bool{
        return lhs->act_num > rhs->act_num || lhs->att.get(EPA_Speed) > rhs->att.get(EPA_Speed);
    });

    if (attackers.empty())
        return NULL;
    else if (attackers.size() == 1) {
        combat_pet_t* front = attackers.front();
        attackers.pop_front();
        return front;
    } else {
        combat_pet_t* front = attackers.front();
        attackers.pop_front();
        combat_pet_t* second = attackers.front();
        attackers.pop_front();

        combat_pet_t* ret = NULL;
        combat_pet_t* remain = NULL;

        int32_t diff = front->att.get(EPA_Speed) - second->att.get(EPA_Speed);
        if (diff >= 50) {
            ret = front;
            remain = second;
        } else {
            if (rand() % 2 == 0) {
                ret = front;
                remain = second;
            } else {
                ret = second;
                remain = front;
            }
        }

        attackers.emplace_back(remain);
        return ret;
    }

}


combat_pet_t* calc_level_supress(combat_pet_t* caster, combat_pet_t* target, vector<att_mod_t> &mod) {
    combat_pet_t* supressed = NULL;;
    int32_t diff = abs(target->level - caster->level);
    if (diff >= 2) {
        int32_t rate = (diff - 1) * g_game_cfg->LevelPressPVE * 10000;
        int32_t maxrate = g_game_cfg->LevelPressPVEMax * 10000;

        if (caster->unit->info && target->unit->info) {
            rate = (diff - 1) * g_game_cfg->LevelPressPVP * 10000;
            maxrate = g_game_cfg->LevelPressPVPMax * 10000;
        }

        rate = min(maxrate, rate);

        mod.emplace_back(EPA_Attack, 0, -rate);
        mod.emplace_back(EPA_Defend, 0, -rate);

        if (target->level > caster->level)
            supressed = caster;
        else
            supressed = target;
    }
    return supressed;
}
bool calc_hit(combat_pet_t* caster, combat_pet_t* target) {
    int32_t real_hit = 10000 + caster->att.get(EPA_HitProb) - target->att.get(EPA_DodgeProb);
    real_hit = cut(real_hit, 0, 10000);

    return rand() % 10000 < real_hit;
}



void calc_property_supress(EPetProperty p1, EPetProperty p2, double &inc, double &dec) {
    static int table[6] = {
            EPP_Fire, EPP_Animal, EPP_Water, EPP_Plant, EPP_Army, EPP_Sound
    };

    if (p2 == table[p1])
        inc = 0.2;
    if (p1 == table[p2])
        dec = 0.2;
}

int32_t calc_damage(combat_pet_t* caster, combat_pet_t* target) {
    // property supress
    double prop_inc = 0.0f;
    double prop_dec = 0.0f;
    calc_property_supress(EPetProperty(caster->proto->family), EPetProperty(target->proto->family), prop_inc, prop_dec);

    double crit_rate = 1.0f;
    int crit_prob = cut(caster->att.get(EPA_CritProb) - target->att.get(EPA_AntiCritProb), 0, 10000);
    if (prob(crit_prob)) {
        crit_rate += (0.5 + caster->att.get(EPA_CritRate) / 9999.0);
        target->log().crit = true;
    }

    int32_t attack = caster->att.get(EPA_Attack);
    int32_t defend = target->att.get(EPA_Defend);

    double damage_rate = cut(10000 + caster->att.get(EPA_DamageIncrease) - target->att.get(EPA_DamageReduce), 0, 1000000) / 10000.0f;
    double prop_rate = cut<double>(1 + prop_inc - prop_dec, 0, 1.0f);

    int32_t damage = (attack - defend * 0.5) * damage_rate * prop_rate * crit_rate;

    if (damage <= 0)
        damage = 1;

    return damage;
}

void do_combat_reward(int actnum, combat_pet_t* caster) {

	if (s_cookie.can_combatreward) {
		int idx = -1;
		if (actnum >= 0 && actnum < 2) {
			idx = 0;
		} else if (actnum >= 2 && actnum < 4) {
			idx = 1;
		} else if (actnum >= 4 && actnum < 6) {
			idx = 2;
		}
		if (idx >= 0) {
			int randset = g_game_cfg->CombatRewardRandSet[idx];
			if (randset >= 0 && caster->unit && caster->unit->info) {
				player_t* player = s_cookie.players[caster->unit->side];
				if (player && player->n.combatreward_times < g_game_cfg->CombatRewardTimes) {
					int freeslot = -1;
					for (int i = 0; i < 3; ++i) {
						if (player->combatrewards[i] < 0) {
							freeslot = i;
							break;
						}
					}

					if (freeslot >= 0) {
						int num = 0;
						int idx = 0;
						int32_t rewardid = pick_one(randset, &num, &idx);
						if (idx >= 0 && rewardid >= 0) {
							player->combatrewards[freeslot] = rewardid;
							caster->log().rewardid = rewardid;
							++player->n.combatreward_times;
						}
					}
				}
			}
		}
	}
}

bool attack_1(int round, combat_pet_t* caster, combat_pet_t* target) {
    RANGE_LOG("::attack1");
    if (target->mianyi || target->frozen) {
        LOC_LOG("target mainyi or frozen");
        target->log().immunity = true;
        return true;
    }

    // 等级压制
    vector<att_mod_t> mod;
    combat_pet_t* supressed = calc_level_supress(caster, target, mod);
    if (NULL != supressed) {
        supressed->att.add_mods(mod);
        LOC_LOG("%s level supressed", supressed == caster ? "caster" : "target");
    }

    int actnum = 0;
    caster->for_each_skill([&actnum, round, caster, target](combat_skill_t* skill){
        skill->begin_cast(round, ESP_Attack1, caster, target);
        if (skill->active)
        	++actnum;
    });
	do_combat_reward(actnum, caster);

    target->for_each_skill([round, caster, target](combat_skill_t*skill){
        skill->begin_cast(round, ESP_BeAttack1, caster, target);
    });
    caster->att.calc();
    target->att.calc();

    bool hit = calc_hit(caster, target);
    if (hit) {
        target->log().calced_damage = calc_damage(caster, target);
        LOC_LOG("target calced_damage:%d", target->clog().calced_damage);

    } else {
        target->log().miss = true;
        target->log().calced_damage = 0;
        LOC_LOG("target miss");
    }

    caster->for_each_skill([hit, caster, target](combat_skill_t* skill){
        skill->do_cast(ESP_Attack1, caster, target, !hit);
    });
    target->for_each_skill([hit, caster, target](combat_skill_t* skill){
        skill->do_cast(ESP_BeAttack1, caster, target, !hit);
    });

    if (caster->actskill) {
    	caster->multi_cast = true;
    	caster->act_num += caster->actskill->caster_act_num_chg;
    	LOC_LOG("caster act_num:%d", proto->caster_act_num_chg);
    	caster->log_skill(caster->actskill->id, caster->actskilliniter);
    	caster->actskill = NULL;
    	caster->actskilliniter = NULL;
    }
    if (target->actskill) {
    	target->multi_cast = true;
    	target->act_num += target->actskill->target_act_num_chg;
    	LOC_LOG("target act_num:%d", proto->target_act_num_chg);
    	target->log_skill(target->actskill->id, target->actskilliniter);
    	target->actskill = NULL;
    	target->actskilliniter = NULL;
    }


    //end
    target->for_each_skill([caster, target](combat_skill_t*skill){
        skill->end_cast(ESP_BeAttack1, caster, target);
    }, true);
    caster->for_each_skill([caster, target](combat_skill_t* skill){
        skill->end_cast(ESP_Attack1, caster, target);
    }, true);

    // level supress
    if (NULL != supressed)
        supressed->att.dec_mods(mod);

    caster->att.calc();
    target->att.calc();

    int32_t angry_add = 0;
    if (caster->clog().castnum > 0) {
        angry_add = (prob(g_game_cfg->AttackAngryAddProb) ? 1 : 0);
    } else {
        angry_add = (prob(g_game_cfg->SkillAngryAddProb) ? 1 : 0);
    }

    target->unit->angry += angry_add;
    LOC_LOG("target angry:+%d=%d", angry_add, target->unit->angry);

    int caster_total = caster->clog().total_damage - caster->clog().total_heal;
    int target_total = target->clog().total_damage + target->clog().calced_damage - target->clog().total_heal;

    if (caster_total > 0)
        caster->damage(caster_total);
    else if (caster_total < 0)
        caster->heal(-caster_total);

    if (target_total > 0)
        target->damage(target_total);
    else if (target_total < 0)
        target->heal(-target_total);

    LOC_LOG("caster final_heal:%d final_damage:%d",
            caster->clog().total_heal, caster->clog().total_damage);
    LOC_LOG("target final_heal:%d final_damage:%d",
            target->clog().total_heal, target->clog().total_damage);


    int act_prob = caster->att.get(EPA_Speed) * g_game_cfg->ActNumAddPctPerSpeed * 100;
    if (act_prob > 0 && prob(act_prob)) {
        ++caster->act_num;
        caster->multi_cast = true;
    }

    return true;
}

bool attack_2(int round, combat_pet_t* caster, combat_pet_t* target) {
    if (target->mianyi) {
        return true;
    }

    RANGE_LOG("::attack2");

    target->for_each_skill([round, caster, target](combat_skill_t* skill){
        skill->begin_cast(round, ESP_Attack2, caster, target);
    });

    target->for_each_skill([caster, target](combat_skill_t* skill){
        skill->do_cast(ESP_Attack2, caster, target, false);
    });
    if (target->actskill) {
    	target->multi_cast = true;
    	target->act_num += target->actskill->target_act_num_chg;
    	LOC_LOG("target act_num:%d", proto->target_act_num_chg);
    	target->log_skill(target->actskill->id, target->actskilliniter);
    	target->actskill = NULL;
    	target->actskilliniter = NULL;
    }

    target->for_each_skill([caster, target](combat_skill_t* skill){
        skill->end_cast(ESP_Attack2, caster, target);
    }, true);

    int caster_total = caster->clog().total_damage - caster->clog().total_heal;
    int target_total = target->clog().total_damage - target->clog().total_heal;

    if (caster_total > 0)
        caster->damage(caster_total);
    else if (caster_total < 0)
        caster->heal(-caster_total);

    if (target_total > 0)
        target->damage(target_total);
    else if (target_total < 0)
        target->heal(-target_total);

    LOC_LOG("caster final_heal:%d final_damage:%d",
            caster->clog().total_heal, caster->clog().total_damage);
    LOC_LOG("target final_heal:%d final_damage:%d",
            target->clog().total_heal, target->clog().total_damage);

    return true;
}


bool g_combat(unit_t* ud1, unit_t* ud2, cs::OCombat* ocombat, int32_t* ud2hp) {
    RANGE_LOG("::combat");

    combat_unit_t* units[2];
    bool win = false;

    // 初始化combat_unit_t
    do {
        LOC_LOG("init");
        units[0] = new combat_unit_t;
        units[0]->side = 0;
        if (!units[0]->init(ud1))
            break;

        units[1] = new combat_unit_t;
        units[1]->side = 1;
        if (!units[1]->init(ud2))
            break;

        if (ud2hp && units[1]->pet[0]) {
      		units[1]->pet[0]->hp = *ud2hp;
        }

        // 填充unit pet sprite
        for (int i=0; i<2; ++i) {
            auto ounit = ocombat->add_unit();
            auto unit = units[i];
            ounit->set_side(i);
            if (unit->info)
                fill_osimpleplayerinfo(unit->info, ounit->mutable_playerinfo());
            ounit->set_angry(unit->angry);
            if (NULL != unit->sprite)
                ounit->set_sprite(unit->sprite->proto->id);
        }
        for_each_pet(units, [ocombat](combat_pet_t* pet){
            auto opet = ocombat->add_pet();
            opet->set_side(pet->unit->side);
            opet->set_pos(pet->pos);
            opet->set_protoid(pet->proto->id);
            opet->set_name(pet->name);
            opet->set_hp(pet->hp);
            opet->set_level(pet->level);
            opet->set_quality(pet->quality);
            opet->set_combatforce(pet->combatforce);
        });

        // begin
        cs::OCombatAttackProcess obegin_attack;

        for_each_sprite(units, [units, &obegin_attack](combat_sprite_t* sprite){
            if (sprite->check_skill(ESSP_Begin, units)) {
                auto ospriteattack = obegin_attack.add_sprite_attack();
                ospriteattack->set_side(sprite->unit->side);
                for_each_pet(units, [sprite, ospriteattack](combat_pet_t* pet){
                    if (pet->is_affected())
                        //pet->_log.reset();
                        pet->fill_ocombatpeteffect(ospriteattack->add_effect());
                });
            }
        });

        for_each_pet(units, [&obegin_attack](combat_pet_t* pet){
            pet->for_each_skill([pet](combat_skill_t* skill){
                skill->begin_cast(-1, ESP_Combat, pet, NULL);
            });
            pet->for_each_skill([pet](combat_skill_t* skill){
                skill->do_cast(ESP_Combat, pet, NULL, false);
            });

            if (pet->is_affected()) {
                auto osubattackprocess = obegin_attack.add_sub_attack_process();
                pet->fill_ocombatpeteffect(osubattackprocess->mutable_caster_effect());
            }
        });

        if (obegin_attack.sub_attack_process_size() > 0 || obegin_attack.sprite_attack_size() > 0)
            *ocombat->mutable_begin()->add_attack_process() = obegin_attack;

        RANGE_LOG("start rounds");

        int round_num = 0;
        while (live_unit_num(units) == 2 && round_num < 30) {
            ++round_num;
            RANGE_LOG("round:%d", round_num);

            auto oround = ocombat->add_round();

            for_each_pet(units, [oround](combat_pet_t* pet){
                if (pet->is_dead())
                    return;

                pet->act_num = 1;
                pet->multi_cast = false;
                pet->update_buff();
                pet->check_dead();
                if (pet->is_affected()) {
                    pet->fill_ocombatpeteffect(oround->add_buff_effect());
                }
            });

            int attack_num = 0;
            while (true) {
                ++attack_num;
                RANGE_LOG("attack:%d", attack_num);

                auto caster = select_caster(units);
                if (NULL == caster)
                    break;

                auto target = select_target(caster, units);
                if (NULL == target)
                    break;

                LOC_LOG("caster(side:%d pos:%d) target(side:%d pos:%d)",
                        caster->unit->side, caster->pos,
                        target->unit->side, target->pos);

                auto oattackprocess = oround->add_attack_process();
                auto osubattackprocess = oattackprocess->add_sub_attack_process();

                int32_t old_target_angry = target->unit->angry;

                attack_1(round_num, caster, target);
                caster->check_dead();
                target->check_dead();
                caster->fill_ocombatpeteffect(osubattackprocess->mutable_caster_effect());
                target->fill_ocombatpeteffect(osubattackprocess->mutable_target_effect());

                attack_2(round_num, caster, target);
                caster->check_dead();
                target->check_dead();
                if (caster->is_affected() || target->is_affected()) {
                    auto osubattackprocess2 = oattackprocess->add_sub_attack_process();
                    caster->fill_ocombatpeteffect(osubattackprocess2->mutable_target_effect());
                    target->fill_ocombatpeteffect(osubattackprocess2->mutable_caster_effect());
                }

                int32_t new_target_angry = target->unit->angry;
                if (new_target_angry != old_target_angry)
                    osubattackprocess->set_target_angry(new_target_angry);

                --caster->act_num;
                if (0 == caster->act_num)
                    caster->multi_cast = false;

                LOC_LOG("caster act_num:%d", caster->act_num);

                for_each_sprite(units, [oattackprocess, units](combat_sprite_t* sprite){
                    int32_t old_caster_angry = sprite->unit->angry;
                    if (sprite->check_skill(ESP_Attack1, units)) {
                        auto ospriteattack = oattackprocess->add_sprite_attack();
                        ospriteattack->set_side(sprite->unit->side);
                        for_each_pet(units, [sprite, ospriteattack](combat_pet_t* pet){
                            if (pet->is_affected())
                                pet->fill_ocombatpeteffect(ospriteattack->add_effect());
                        });

                        int32_t new_caster_angry = sprite->unit->angry;
                        if (old_caster_angry != new_caster_angry)
                            ospriteattack->set_caster_angry(new_caster_angry);
                    }
                });
            }
        }

        // end_combat
        for_each_pet(units, [](combat_pet_t* pet){
            pet->for_each_skill([pet](combat_skill_t* skill){
                skill->end_cast(ESSP_Attack, pet, NULL);
            });
        }, true);

        for_each_sprite(units, [units](combat_sprite_t*sprite){
            sprite->check_skill(ESSP_End, units);
        }, true);

        win = live_num(units[0]) > 0 && round_num < 30;
        ocombat->set_win(win);

        LOC_LOG("win:%d", win);
    } while (0);

    if (ud2hp && units[1]->pet[0]) {
    	*ud2hp = units[1]->pet[0]->hp;
    }

    for (int i=0; i<2; ++i) {
        if (NULL == units[i])
            continue;

        units[i]->destroy();
        delete units[i];
        units[i] = NULL;
    }


    return win;
}


bool combat_sprite_t::init(sprite_t* sprite) {
    proto = sprite->proto;
    for (int i=0; i<SpriteSkillNum; ++i)
        skill_level[i] = sprite->skill_level[i];

    LOC_LOG("combat_sprite_t::init proto:%d skilllevel:%d,%d", proto->id, skill_level[0], skill_level[1]);
    return true;
}
void combat_sprite_t::destroy() {
    proto = NULL;
}

bool combat_sprite_t::check_skill(int essp, combat_unit_t*const* units) {
    bool trigger = false;

    for (int i=0; i<SpriteSkillNum; ++i) {
        const tagProtoSpriteSkill* skill_proto = MAProto(SpriteSkill, proto->skill[i]);
        if (NULL == skill_proto)
            continue;

        if (essp != skill_proto->point)
            continue;

        if (essp == ESSP_Attack) {
            if (unit->angry < 7)
                continue;

            unit->angry = 0;
        }

        RANGE_LOG("combat_sprite_t::check_skill point:%d proto:%d skill_proto:%d level:%d",
                essp, proto->id, skill_proto->id, skill_level[i]);

        if (skill_proto->att >= 0) {
            vector<att_mod_t> mods;
            mods.emplace_back(skill_proto->att, 0, skill_proto->init_pct + (skill_level[i] - 1) * skill_proto->inc_pct);

            auto self_unit = unit;
            for_each_pet(units, [self_unit, &mods, skill_proto](combat_pet_t* pet){
                if (pet->is_dead())
                    return;

                if (skill_proto->self && self_unit->side == pet->unit->side) {
                    log_record &effect = pet->log_spriteskill(skill_proto->id, self_unit->side);
                    effect.sprite_skillid = skill_proto->id;
                    effect.att = mods.front().att;
                    effect.flat = mods.front().flat_mod;
                    effect.pct = mods.front().pct_mod;

                    int oldhp = pet->att.get(EPA_Hp);
                    pet->att.add_mods(mods);
                    pet->att.calc();
                    int newhp = pet->att.get(EPA_Hp);
                    pet->hp = newhp;
                    int diff = newhp - oldhp;
                    if (diff > 0) {
                        effect.heal = diff;
                    } else if (diff < 0) {
                        effect.damage = -diff;
                    }
                    LOC_LOG("pet side:%d pos:%d att:%d pct:%d flat:%d",
                            pet->unit->side, pet->pos, effect.att, effect.pct, effect.flat);
                }

                if (!skill_proto->self && self_unit->side != pet->unit->side) {
                    log_record &effect = pet->log_spriteskill(skill_proto->id, self_unit->side);
                    effect.sprite_skillid = skill_proto->id;
                    effect.att = mods.front().att;
                    effect.flat = mods.front().flat_mod;
                    effect.pct = mods.front().pct_mod;

                    int oldhp = pet->att.get(EPA_Hp);
                    pet->att.add_mods(mods);
                    pet->att.calc();
                    int newhp = pet->att.get(EPA_Hp);
                    pet->hp = newhp;
                    int diff = newhp - oldhp;
                    if (diff > 0) {
                        effect.heal = diff;
                        pet->log().total_heal += diff;
                    } else if (diff < 0) {
                        effect.damage = -diff;
                        pet->log().total_damage -= diff;
                    }
                    LOC_LOG("pet side:%d pos:%d att:%d pct:%d flat:%d",
                            pet->unit->side, pet->pos, effect.att, effect.pct, effect.flat);
                }
            });
            trigger = true;
        }

        if (skill_proto->init_heal > 0) {
            int32_t value = skill_proto->init_heal + (skill_level[i] - 1) * skill_proto->inc_heal;

            auto self_unit = unit;
            for_each_pet(units, [self_unit, skill_proto, value](combat_pet_t* pet){
                if (pet->is_dead())
                    return;

                if (skill_proto->self && self_unit->side == pet->unit->side) {
                    log_record &effect = pet->log_spriteskill(skill_proto->id, self_unit->side);
                    effect.sprite_skillid = skill_proto->id;
                    effect.heal = value;
                    pet->log().total_heal += value;
                    pet->heal(value);
                    LOC_LOG("pet side:%d pos:%d heal:%d",
                            pet->unit->side, pet->pos, value);

                }
                if (!skill_proto->self && self_unit->side != pet->unit->side) {
                    log_record &effect = pet->log_spriteskill(skill_proto->id, self_unit->side);
                    effect.sprite_skillid = skill_proto->id;
                    effect.heal = value;
                    pet->log().total_heal += value;
                    pet->heal(value);
                    LOC_LOG("pet side:%d pos:%d heal:%d",
                            pet->unit->side, pet->pos, value);
                }

            });
            trigger = true;
        }

        if (skill_proto->init_damage > 0) {
            int32_t value = skill_proto->init_damage + (skill_level[i] - 1) * skill_proto->inc_damage;

            auto self_unit = unit;
            for_each_pet(units, [self_unit, skill_proto, value](combat_pet_t* pet){
                if (pet->is_dead())
                    return;

                if (skill_proto->self && self_unit->side == pet->unit->side) {
                    int32_t defend = pet->att.final[EPA_Defend];
                    int32_t damage = max(value - defend, 0);

                    log_record &effect = pet->log_spriteskill(skill_proto->id, self_unit->side);
                    effect.sprite_skillid = skill_proto->id;
                    effect.damage = damage;
                    pet->log().total_damage += damage;
                    pet->damage(damage);
                    LOC_LOG("pet side:%d pos:%d damage:%d",
                            pet->unit->side, pet->pos, damage);
                }
                if (!skill_proto->self && self_unit->side != pet->unit->side) {
                    int32_t defend = pet->att.final[EPA_Defend];
                    int32_t damage = max(value, 0);

                    log_record &effect = pet->log_spriteskill(skill_proto->id, self_unit->side);
                    effect.sprite_skillid = skill_proto->id;
                    effect.damage = damage;
                    pet->log().total_damage += damage;
                    pet->damage(damage);
                    LOC_LOG("pet side:%d pos:%d damage:%d",
                            pet->unit->side, pet->pos, damage);
                }
            });
            trigger = true;
        }
    }

    return trigger;
}


void combat_skill_t::begin_cast(int round, int esp, combat_pet_t* caster, combat_pet_t* target) {
    if (proto->point != esp)
        return;

    active = false;

    auto initer = (ESP_BeAttack1 == esp || ESP_Attack2 == esp) ? target : caster;
    if (initer->dummy)
        return;

    if (proto->cast_round > 0 && round > 0) {
    	if (round % proto->cast_round != 0)
    		return;
    }

    if (proto->cast_num >= 0 && cast_num >= proto->cast_num)
        return;

    if (proto->prev_skillid >= 0) {
        auto skill = initer->skill.get(proto->prev_skillid);
        if (NULL == skill)
            return;

        if (!skill->active)
            return;
    }

    if (rand() % 10000 >= proto->prob)
        return;

    if (proto->caster_hp_pct_low >= 0) {
        double hp_pct = proto->caster_hp_pct_low / 10000.0f;
        int32_t hp_limit = hp_pct * caster->att.get(EPA_Hp);
        if (caster->hp > hp_limit)
            return;
    }

    if (proto->caster_hp_pct_high >= 0) {
        double hp_pct = proto->caster_hp_pct_high / 10000.0f;
        int32_t hp_limit = hp_pct * caster->att.get(EPA_Hp);
        if (caster->hp <= hp_limit)
            return;
    }

    if (proto->target_hp_pct_low >= 0) {
        double hp_pct = proto->target_hp_pct_low / 10000.0f;
        int32_t hp_limit = hp_pct * target->att.get(EPA_Hp);
        if (target->hp > hp_limit)
            return;
    }

    if (proto->target_hp_pct_high >= 0) {
        double hp_pct = proto->target_hp_pct_high / 10000.0f;
        int32_t hp_limit = hp_pct * target->att.get(EPA_Hp);
        if (target->hp <= hp_limit)
            return;
    }

    initer->log_skill(proto->id, initer);

    active = true;
    ++cast_num;
    RANGE_LOG("combat_skill_t::begin_cast point:%d cast_num:%d caster(side:%d pos%d) target(side:%d pos:%d)",
            esp, cast_num, caster->unit->side, caster->pos, target != NULL ? target->unit->side : -1, target != NULL ? target->pos : -1);

    if (proto->caster_attmodid >= 0) {
        caster->log_skill(proto->id, initer);
        caster->att.add_mod(proto->caster_attmodid);
        LOC_LOG("caster attmod:+%d", proto->caster_attmodid);
    }

    if (proto->target_attmodid >= 0 && target != NULL) {
        target->log_skill(proto->id, initer);
        target->att.add_mod(proto->target_attmodid);
        LOC_LOG("target attmod:+%d", proto->target_attmodid);
    }
}
void combat_skill_t::end_cast(int esp, combat_pet_t* caster, combat_pet_t* target) {
    if (proto->point != esp)
        return;

    if (!active)
        return;

    RANGE_LOG("combat_skill_t::end_cast");

    if (proto->caster_attmodid >= 0) {
        caster->att.dec_mod(proto->caster_attmodid);
        LOC_LOG("caster attmod:-%d", proto->caster_attmodid);
    }

    if (proto->target_attmodid >= 0 && NULL != target) {
        target->att.dec_mod(proto->target_attmodid);
        LOC_LOG("target attmod:-%d", proto->target_attmodid);
    }

    active = false;
}

void combat_skill_t::do_cast(int esp, combat_pet_t* caster, combat_pet_t* target, bool dodge) {
    if (proto->point != esp)
        return;

    if (!active)
        return;

    RANGE_LOG("combat_skill_t::do_cast");

    auto initer = (ESP_BeAttack1 == esp || ESP_Attack2 == esp) ? target : caster;

    if (proto->caster_buffid >= 0 && caster->add_buff(proto->caster_buffid, initer)) {
        caster->log_skill(proto->id, initer)
                .buff_added.push_back(proto->caster_buffid);
    }
    if (proto->caster_act_num_chg != 0) {
    	if (!caster->dizzy && !caster->dummy) {
    		bool ok = false;
    		if (!caster->actskill) {
    			ok = true;
    		} else if (caster->actskill && proto->caster_act_num_chg > caster->actskill->caster_act_num_chg) {
    			ok = true;
    		}
    		if (ok) {
    			caster->actskill = proto;
    			caster->actskilliniter = initer;

//                caster->multi_cast = true;
//                caster->act_num += proto->caster_act_num_chg;
//                LOC_LOG("caster act_num:%d", proto->caster_act_num_chg);
//                caster->log_skill(proto->id, initer);
    		}
    	}
    }
    if (proto->caster_damage_valueid >= 0) {
        int32_t value = calc_value(proto->caster_damage_valueid, caster, initer);
        caster->log_skill(proto->id, initer).damage = value;
        caster->log().total_damage += value;
        LOC_LOG("caster damage:%d", value);
    }
    if (proto->caster_heal_valueid >= 0) {
        int32_t value = calc_value(proto->caster_heal_valueid, caster, initer);
        caster->log_skill(proto->id, initer).heal = value;
        caster->log().total_heal += value;
        LOC_LOG("caster heal:%d", value);
    }
    if (proto->caster_purge) {
        LOC_LOG("caster purge");
        caster->clear_allbuff(&caster->log_skill(proto->id, initer).buff_removed, initer == caster);
    }

    if (proto->target_buffid >= 0 && target->add_buff(proto->target_buffid, initer)) {
        target->log_skill(proto->id, initer)
                .buff_added.push_back(proto->target_buffid);
    }
    if (proto->target_act_num_chg != 0) {
    	if (!target->dizzy && !target->dummy) {
    		bool ok = false;
    		if (!target->actskill) {
    			ok = true;
    		} else if (target->actskill && proto->target_act_num_chg > target->actskill->target_act_num_chg) {
    			ok = true;
    		}
    		if (ok) {
    			target->actskill = proto;
    			target->actskilliniter = initer;
    		}
    	}
    }
    if (proto->target_damage_valueid >= 0) {
        int32_t value = calc_value(proto->target_damage_valueid, target, initer);
        target->log_skill(proto->id, initer).damage = value;
        target->log().total_damage += value;
        LOC_LOG("target damage:%d", value);
    }
    if (proto->target_heal_valueid >= 0) {
        int32_t value = calc_value(proto->target_heal_valueid, target, initer);
        target->log_skill(proto->id, initer).heal = value;
        target->log().total_heal += value;
        LOC_LOG("target heal:%d", value);
    }
    if (proto->target_purge) {
        LOC_LOG("target purge");
        target->clear_allbuff(&target->log_skill(proto->id, initer).buff_removed, initer == target);
    }
}


void combat_buff_t::on_add() {
    if (proto->attmodid >= 0)
        owner->att.add_mod(proto->attmodid);
}

void combat_buff_t::on_cancel() {
    if (proto->attmodid >= 0)
        owner->att.dec_mod(proto->attmodid);
}

void combat_buff_t::on_remove() {
    if (proto->attmodid >= 0)
        owner->att.dec_mod(proto->attmodid);

    RANGE_LOG("combat_buff_t::on_remove proto:%d", proto->id);

    if (EBP_Remove == proto->effect_point) {
        if (proto->damage_valueid >= 0) {
            int32_t value = calc_value(proto->damage_valueid, owner, adder);

            owner->log_buff(proto->id, owner).damage = value;
            owner->log().total_damage += value;
            LOC_LOG("damage:%d", value);
        }

        if (proto->heal_valueid >= 0) {
            int32_t value = calc_value(proto->heal_valueid, owner, adder);

            owner->log_buff(proto->id, owner).heal = value;
            owner->log().total_heal += value;
            LOC_LOG("heal:%d", value);
        }

    }

    owner->log().buff_removed.push_back(proto->id);
}

void combat_buff_t::on_round() {
    if (proto->effect_point != EBP_Round)
        return;

    RANGE_LOG("combat_buff_t::on_round proto:%d", proto->id);

    if (proto->damage_valueid >= 0) {
        int32_t value = calc_value(proto->damage_valueid, owner, adder);
        owner->log_buff(proto->id, owner).damage = value;
        owner->log().total_damage += value;
        LOC_LOG("damage:%d", value);
    }

    if (proto->heal_valueid >= 0) {
        int32_t value = calc_value(proto->heal_valueid, owner, adder);
        owner->log_buff(proto->id, owner).heal = value;
        owner->log().total_heal += value;
        LOC_LOG("heal:%d", value);
    }
}

bool combat_pet_t::init(pet_t* pet, const vector<att_mod_t> &mods, bool player) {
    RANGE_LOG("combat_pet_t::init side:%d pos:%d", unit->side, pos);

    proto = pet->proto;
    level = pet->att(Pe4s_Level);
    quality = (EQuality)proto->quality;
    name = pet->att(Pess_Name);
    combatforce = pet->att(Pe4s_CombatForce);

    int tmp_att[CanSeePetAttNum];
    for (int i=0; i<CanSeePetAttNum; ++i)
        tmp_att[i] = pet->att(EPeAtt4Save(Pe4s_AttBegin + i));

    att.init(tmp_att);

    if (NULL != pet->def_att) {
        for (int i=0; i<EPetAtt_ARRAYSIZE; ++i)
            att.base[i] += pet->def_att[i];
    }

    stringstream ss;

    for (int i=0; i<PetSpecSkillNum; ++i) {
        int32_t skillid = pet->att(EPeAtt4Save(Pe4s_SkillSSlotBegin + i));
        if (skillid < 0)
            continue;

        const tagProtoSkill* skill_proto = MAProto(Skill, skillid);
        if (NULL == skill_proto)
            continue;

        auto skill = this->skill[skill_proto->id / 10];
        if (NULL != skill)
            continue;

        skill = new combat_skill_t;
        skill->proto = skill_proto;
        this->skill[skill_proto->id / 10] = skill;
        ss << skill_proto->id << ",";
    }


    // 魔法印记效果
    for (int i=0; i<PetNormalSkillNum; ++i) {
        int32_t skillid = pet->att(EPeAtt4Save(Pe4s_SkillNSlotBegin + i));
        if (skillid < 0)
            continue;

        const tagProtoSkill* skill_proto = MAProto(Skill, skillid);
        if (NULL == skill_proto)
            continue;

        if (skill_proto->family >= 0)
            continue;

        if (skill_proto->caster_attmodid < 0)
            continue;

        att.add_mod(skill_proto->caster_attmodid, 1);
    }

    if (player) {
    	const auto* curproto = proto;
    	while (curproto) {
    		if (curproto->attmod >= 0)
    			att.add_mod(curproto->attmod);
    		auto itr = g_svr.protomgr->map_pet_evofrom.find(curproto->id);
    		if (itr != g_svr.protomgr->map_pet_evofrom.end()) {
    			curproto = MAProto(Pet, itr->second);
    		} else {
    			curproto = NULL;
    		}
    	}
    }

    att.add_mods(mods);

    hp = att.get(EPA_Hp);

    LOC_LOG("proto:%d level:%d hp:%d skill:%s", proto->id, level, hp, ss.str().c_str());

    return true;
}

void combat_pet_t::destroy() {
    for_each_skill([](combat_skill_t* skill){
        safe_del(skill);
    });
    skill.clear();

    for (auto itr = buff.begin(); itr != buff.end(); ++itr)
        safe_del(itr->second);
    buff.clear();

    att.destroy();
}

void combat_pet_t::for_each_skill(function<void(combat_skill_t*)> cb, bool reverse) {
    if (reverse)
        for (auto itr = skill.rbegin(); itr != skill.rend(); ++itr)
            cb(itr->second);
    else
        for (auto itr = skill.begin(); itr != skill.end(); ++itr)
            cb(itr->second);

}

void combat_pet_t::update_buff() {
    RANGE_LOG("combat_pet_t::update_buff side:%d pos:%d", unit->side, pos);

    list<int32_t> need_remove;
    for (auto itr = buff.begin(); itr != buff.end(); ++itr) {
        combat_buff_t* cur_buff = itr->second;

        if (NULL == cur_buff)
            need_remove.push_back(itr->first);
        else {
            cur_buff->on_round();
            ++cur_buff->round;
            if (cur_buff->round >= cur_buff->proto->round_num)
                need_remove.push_back(itr->first);
        }
    }

    stringstream ss;
    while (!need_remove.empty()) {
        int32_t protoid = need_remove.front();

        if (buff.count(protoid) > 0) {
            auto cur_buff = buff[protoid];
            cur_buff->on_remove();
            delete cur_buff;
        }

        buff.erase(protoid);

        need_remove.pop_front();
        ss << protoid << ",";
    }
    LOC_LOG("removed:", ss.str().c_str());

    _update_status();

    int total = this->clog().total_damage - this->clog().total_heal;

    if (total > 0)
        this->damage(total);
    else if (total < 0)
        this->heal(-total);

    LOC_LOG("total_damage:%d total_heal:%d", this->clog().total_damage, this->clog().total_heal);
    LOC_LOG("dummy:%d dizzy:%d frozen:%d immunity:%d",
            this->dummy, this->dizzy, this->frozen, this->mianyi);

    att.calc();
}

void combat_pet_t::check_dead() {
    if (this->hp > 0)
        return;

    if (!buff.empty())
        clear_allbuff(&log().buff_removed, true);
}

void combat_pet_t::clear_allbuff(vector<int32_t>* ids, bool self) {
    RANGE_LOG("combat_pet_t::clear_allbuff self:%d", self);

    stringstream ss;
    for (auto itr = buff.begin();
        itr != buff.end();
        ) {

        auto cur_itr = itr++;
        combat_buff_t* cur_buff = cur_itr->second;
        auto proto = cur_buff->proto;

        cur_buff->on_cancel();
        buff.erase(cur_itr);
        delete cur_buff;

        if (NULL != ids)
            ids->push_back(proto->id);
        ss << proto->id << ",";
    }

    LOC_LOG("removed:%s", ss.str().c_str());

    _update_status();

    LOC_LOG("dummy:%d dizzy:%d frozen:%d immunity:%d",
            this->dummy, this->dizzy, this->frozen, this->mianyi);
}

void combat_pet_t::_update_status() {
    mianyi = false;
    dizzy = false;
    dummy = false;
    frozen = false;
    for (auto itr = buff.begin(); itr != buff.end(); ++itr) {
        combat_buff_t* cur_buff = itr->second;
        if (cur_buff->proto->immunity) {
            mianyi = true;
        }
        if (cur_buff->proto->dizzy) {
            dizzy = true;
        }
        if (cur_buff->proto->dummy) {
            dummy = true;
        }
        if (cur_buff->proto->frozen) {
            frozen = true;
        }
    }

    if (dizzy)
        act_num = 0;
    if (frozen)
        act_num = 0;
}


bool combat_pet_t::add_buff(int32_t buffid, combat_pet_t* adder) {
    const tagProtoBuff* buff_proto = MAProto(Buff, buffid);
    if (NULL == buff_proto)
        return false;

    RANGE_LOG("combat_pet_t::add_buff side:%d pos:%d buff:%d", unit->side, pos, buffid);

    if (this->buff.count(buff_proto->id) <= 0) {
        auto buff = new combat_buff_t;
        buff->proto = buff_proto;
        buff->owner = this;
        buff->adder = adder;
        buff->num = 1;
        this->buff.insert(make_pair(buff_proto->id, buff));

        buff->on_add();

        _update_status();

        return true;
    } else if (buff_proto->overlap) {
        combat_buff_t* buff = this->buff[buff_proto->id];
        ++buff->num;
        buff->round = 0;
        return false;
    }

}

void combat_unit_t::gather_mods(unit_t* unit) {
    player_t* player = dynamic_cast<player_t*>(unit);
    if (NULL == player)
        return;


    for (int i = 0; i < CanSeePetAttNum; ++i) {
        int value = player->att(EPlAtt4Save(i+Pl4s_EquipAttBegin));
        int att = i;

        mods.emplace_back(i, value, 0);
    }

}

bool combat_unit_t::init(unit_t* unit) {
    RANGE_LOG("combat_unit_t::init side:%d", side);
    gather_mods(unit);
    auto player = dynamic_cast<player_t*>(unit);

    for (int i=0; i<PlayerCombatCSlotNum; ++i) {
        auto serial = unit->combat[i];
        if (serial < 0)
            pet[i] = NULL;
        else {
            pet[i] = new combat_pet_t();
            pet[i]->unit = this;
            pet[i]->pos = i;
            if (!pet[i]->init(unit->pets[serial], mods, player != NULL)) {
                pet[i]->destroy();
                delete pet[i];
                pet[i] = NULL;

                return false;
            }
        }
    }

    if (unit->cur_sprite >= 0) {
        auto sprite = unit->sprites.get(unit->cur_sprite);
        if (NULL != sprite) {
            this->sprite = new combat_sprite_t();
            this->sprite->unit = this;
            if (!this->sprite->init(sprite)) {
                sprite->destroy();
                delete sprite;
                sprite = NULL;

                return false;
            }
        }
    }

    this->angry = 0;
    const tagProtoMedal* medal_proto = NULL;
    if (NULL != player) {
        this->info = g_playermgr->infos.get(player->id);
        const auto* form_proto = ARProto(SpriteFormLevel, player->att(Pl4s_FormLevel));
        if (form_proto && form_proto->att_num > 0 && player->cur_sprite >= 0) {
        	int32_t stepseed = player->att(Pl4s_FormSeed);
        	for (int i=0; i<form_proto->att_num; ++i) {
        		int idx = i % 3;
        		EPetAtt att = EPetAtt(random(stepseed) % (EPetAtt_ARRAYSIZE + PetAptitudeNum));
        		auto curpet = pet[idx];
        		if (!curpet)
        			continue;

        		int pctatt = att - EPetAtt_ARRAYSIZE;
        		if (EPetAtt_IsValid(att)) {
        			curpet->att.mod_flat(att, form_proto->att_value[att], true);
        		} else if (pctatt >= 0 && pctatt < PetAptitudeNum) {
        			curpet->att.mod_pct(EPetAtt(pctatt), form_proto->att_value[att], true);
        		}
        	}
        }
        medal_proto = ARProto(Medal, player->att(Pl4s_Medal));
    }

	for (int i=0; i<PlayerCombatCSlotNum; ++i) {
		if (!pet[i])
			continue;

		if (medal_proto && medal_proto->attmod >= 0)
			pet[i]->att.add_mod(medal_proto->attmod);

		pet[i]->att.calc();
		pet[i]->hp = pet[i]->att.get(EPA_Hp);
    }

    LOC_LOG("angry:%d name:%s", angry, name.c_str());

    return true;
}
void combat_unit_t::destroy() {
    for (int i=0; i<3; ++i) {
        if (NULL != pet[i]) {
            pet[i]->destroy();
            delete pet[i];
            pet[i] = NULL;
        }
    }
    if (NULL != sprite) {
        sprite->destroy();
        delete sprite;
        sprite = NULL;
    }
}


log_record &combat_pet_t::log_skill(int32_t id, combat_pet_t* initer) {
    bool self = (initer == this);
    // 若是自己，那么日志里面的id为正，否则为负，以区别自己打还是被打的效果
    int32_t lid = (self ? id : -id);

    auto itr = _log.effect.find(lid);
    if (_log.effect.end() == itr) {
        _log.effect[lid];
        itr = _log.effect.find(lid);
        if (self)
            ++_log.castnum;
    }

    itr->second.self_cast = self;
    itr->second.skillid = id;

    _log.affected = true;

    return itr->second;
}
log_record &combat_pet_t::log_buff(int32_t id, combat_pet_t* initer) {
    bool self = (initer == this);
    int32_t lid = (self ? id : -id);

    log_record &ret = _log.effect[lid];
    ret.self_cast = self;
    ret.buffid = id;

    _log.affected = true;
    return ret;
}

log_record &combat_pet_t::log_spriteskill(int32_t id, int32_t side) {
    bool self = (side == this->unit->side);
    int32_t lid = (self ? id : -id);

    log_record &ret = _log.effect[lid];
    ret.self_cast = self;
    ret.sprite_skillid = id;

    _log.affected = true;
    return ret;
}

void combat_pet_t::fill_ocombatpeteffect(cs::OCombatPetEffect* oeffect) {
    oeffect->set_side(unit->side);
    oeffect->set_pos(pos);
    oeffect->set_cur_hp(hp);

    if (_log.rewardid >= 0)
    	oeffect->set_reward(_log.rewardid);

    if (_log.calced_damage > 0)
        oeffect->set_calc_damage(_log.calced_damage);

    if (_log.crit)
        oeffect->set_crit(true);

    if (_log.miss)
        oeffect->set_miss(true);

    if (_log.immunity)
        oeffect->set_immunity(true);

    for (auto itr = _log.effect.begin(); itr != _log.effect.end(); ++itr) {
        log_record &effect = itr->second;

        auto loeffect = oeffect->add_effect();
        if (effect.buffid >= 0)
            loeffect->set_id(effect.buffid);
        else if (effect.skillid >= 0) {
            loeffect->set_id(effect.skillid);
            loeffect->set_self(effect.self_cast);
        } else
            loeffect->set_id(effect.sprite_skillid);

        if (effect.damage > 0)
            loeffect->set_damage(effect.damage);
        if (effect.heal > 0)
            loeffect->set_heal(effect.heal);
        if (effect.att >= 0) {
            loeffect->set_att(effect.att);
            loeffect->set_flat(effect.flat);
            loeffect->set_pct(effect.pct);
        }

        for (auto itr = effect.buff_added.begin();
            itr != effect.buff_added.end();
            ++itr) {
            loeffect->add_added_buff(*itr);
        }

        for (auto itr = effect.buff_removed.begin();
            itr != effect.buff_removed.end();
            ++itr) {
            loeffect->add_removed_buff(*itr);
        }
    }

    for (auto itr = _log.buff_removed.begin(); itr != _log.buff_removed.end(); ++itr) {
        oeffect->add_removed_buff(*itr);
    }

    _log.reset();
}


bool test_combat(cs::OCombat* ocombat) {
    player_t player_data;
    level_t ld1;
    level_t ld2;

    if (!player_data.init(1, "player1", EPS_Male, "haha")) {
        return false;
    }

    if (!ld1.init_level(10000)) {
        ld1.destroy();
        return false;
    }
    if (!ld2.init_level(10000)) {
        ld1.destroy();
        return false;
    }

    auto pp = new pet_t;
    if (!pp->init(int32_t(101), player_data.n.valid_pet_serial++)) {
        --player_data.n.valid_pet_serial;
        pp->destroy();
        delete pp;
        return false;
    }
    player_data.add_pet(pp);
    player_data.combat[0] = pp->serial;

    auto sp = new sprite_t;
    if (!sp->init(3, player_data.n.valid_sprite_serial++)) {
        --player_data.n.valid_sprite_serial;
        sp->destroy();
        delete sp;
        return false;
    }

    player_data.add_sprite(sp);
    player_data.att(Pl4s_CurSprite, sp->serial);

    g_combat(&ld2, &ld1, ocombat);

    ld1.destroy();
    ld2.destroy();

    //ocombat->SerializeAsString();
    //printf("%s\n", ocombat->DebugString().c_str());

    return true;
}

bool g_combat(player_t* player, int32_t level, cs::OCombat* ocombat) {
    level_t lvl;
    if (!lvl.init_level(level)) {
        lvl.destroy();
        return false;
    }

    bool result = g_combat(player, &lvl, ocombat);

    if (result) {
        const tagProtoLevel* level_proto = MAProto(Level, level);
        if (NULL != level_proto) {
            player->reward(level_proto->reward, ocombat->mutable_reward());
        }
    }

    // 宠物图鉴
    lvl.pets.for_each([player](int32_t serial, pet_t* &pet){
        if (pet->proto->cardid < 0)
            return;
        if (player->petseen.count(pet->proto->cardid) > 0)
            return;

        player->petseen.insert(pet->proto->cardid);
        player->petseen_added.push_back(pet->proto->cardid);
    });

    lvl.destroy();
    return result;
}

bool g_combat(player_t* player, pet_t* pet, int32_t level, cs::OCombat* ocombat) {
    level_t lvl;
    if (!lvl.init_level(level)) {
        lvl.destroy();
        return false;
    }

    bool result = false;

    {
        player_t playerpet;
        playerpet.id = player->id;
    	playerpet.add_pet(pet, false);
    	playerpet.att(Pl4s_CombatC1, pet->serial);
        result = g_combat(&playerpet, &lvl, ocombat);
        playerpet.remove_pet(pet->serial);
    }

    if (result) {
        const tagProtoLevel* level_proto = MAProto(Level, level);
        if (NULL != level_proto) {
            player->reward(level_proto->reward, ocombat->mutable_reward(), 1.0f, pet->serial);
        }
    }

    // 宠物图鉴
    lvl.pets.for_each([player](int32_t serial, pet_t* &pet){
        if (pet->proto->cardid < 0)
            return;
        if (player->petseen.count(pet->proto->cardid) > 0)
            return;

        player->petseen.insert(pet->proto->cardid);
        player->petseen_added.push_back(pet->proto->cardid);
    });

    lvl.destroy();
    return result;
}

